package cn.wizzer.app.user.modules.service.impl;

import cn.wizzer.app.user.modules.models.RedPacket;
import cn.wizzer.app.user.modules.models.RedPacketRecord;
import cn.wizzer.app.user.modules.models.enums.BizTypeEnum;
import cn.wizzer.app.user.modules.models.enums.RedPacketStatusEnum;
import cn.wizzer.app.user.modules.models.enums.RedPacketTypeEnum;
import cn.wizzer.app.user.modules.models.enums.UserColEnum;
import cn.wizzer.app.user.modules.models.vo.RedPacketRecordVO;
import cn.wizzer.app.user.modules.models.vo.RedPacketVO;
import cn.wizzer.app.user.modules.services.GroupLogService;
import cn.wizzer.app.user.modules.services.RedPacketRecordService;
import cn.wizzer.app.user.modules.services.RedPacketService;
import cn.wizzer.app.user.modules.services.account.UserAccountRecordService;
import cn.wizzer.app.user.modules.services.account.UserAccountService;
import cn.wizzer.app.user.modules.services.account.UserRechargeService;
import cn.wizzer.framework.base.model.enums.BizExceptionEnum;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.base.service.CommonDBService;
import cn.wizzer.framework.page.Pagination;
import cn.wizzer.framework.util.Id;
import cn.wizzer.framework.util.Result;
import com.alibaba.dubbo.config.annotation.Service;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@IocBean(args = {"refer:dao"})
@Service(interfaceClass = RedPacketService.class)
public class RedPacketServiceImpl extends BaseServiceImpl<RedPacket> implements RedPacketService {
    public RedPacketServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private RedPacketRecordService redPacketRecordService;

    @Inject
    private UserAccountRecordService userAccountRecordService;

    @Inject
    private GroupLogService groupLogService;


    @Inject
    private CommonDBService dbService;

    @Inject
    private UserRechargeService userRechargeService;

    @Aop(TransAop.REPEATABLE_READ)
    @Override
    public RedPacket send(String userId, String currencyId, Integer quantity, RedPacketTypeEnum type, BigDecimal total, String wish) {
        // 校验用户钱包余额是否充足

        // 校验红包个数不能大于100
        if (quantity > 100 || quantity < 1) {
            Result.breakWith(BizExceptionEnum.RED_PACKET_QUANTITY_ERROR);
        }
        // 红包金额必须大于1
        if (total.compareTo(new BigDecimal(1)) < 0) {
            Result.breakWith(BizExceptionEnum.RED_PACKET_TOTAL_ERROR);
        }
        // 生成红包
        RedPacket redPacket = new RedPacket();
        redPacket.setUserId(userId);
        redPacket.setNo(Id.createOrderNo());
        redPacket.setCurrencyId(currencyId);
        redPacket.setQuantity(quantity);
        redPacket.setType(type);
        redPacket.setStatus(RedPacketStatusEnum.OPENDING);
        redPacket.setTotal(total);
        redPacket.setWish(wish);
        this.insert(redPacket);

        // 生成红包记录
        redPacketRecordService.generate(redPacket);

        // 扣掉余额，插入流水
        userAccountRecordService.add(currencyId, userId, total, BizTypeEnum.SEND_RED_PACKET, redPacket.getId(), redPacket.getNo());
        return redPacket;
    }

    @Aop(TransAop.REPEATABLE_READ)
    @Override
    public RedPacketRecordVO open(String userId, String redPacketId) {
        //
        RedPacket redPacket = this.fetch(redPacketId);
        // 查找自己是否抢过，抢过就不用判断是不是过期等状态了
        RedPacketRecordVO redPacketRecordVO = redPacketRecordService.fetchByRedIdUserId(redPacketId, userId);
        if (redPacketRecordVO != null) {
            return redPacketRecordVO;
        }
        // 当红包属于未抢完或未过期才能抢，还需要判断自己是不是已经抢过了，如果抢过了，还需要给出状态
        if (redPacket.getStatus().equals(RedPacketStatusEnum.OPENDING)) {
            List<RedPacketRecordVO> list = redPacketRecordService.listByAsc(redPacketId);
            if (list.size() > 0) {
                RedPacketRecordVO vo = list.get(0);
                RedPacketRecord redPacketRecord = list.get(0);
                redPacketRecord.setUserId(userId);
                redPacketRecord.setOpAt(Times.getTS());
                redPacketRecord.setOpenAt(Times.getTS());
                redPacketRecordService.update(redPacketRecord);
                // 更新已抢的金额
                redPacket.setOpened(redPacket.getOpened().add(redPacketRecord.getAmount()));
                // 抢红包，插入流水
                userAccountRecordService.add(redPacket.getCurrencyId(), userId, redPacketRecord.getAmount(), BizTypeEnum.RECEIVED_RED_PACKET, redPacketRecord.getId(), redPacketRecord.getNo());
                // 最后一个被打开就需要更新红包状态为opened
                if (list.size() == 1) {
                    // 更新红包记录已被领取 opened 金额
                    redPacket.setStatus(RedPacketStatusEnum.OPENDED);
                    // 同时需要更新消息记录
                    groupLogService.updateRedPacket(redPacketId, RedPacketStatusEnum.OPENDED);
                }
                this.update(redPacket);
                // 返回红包记录
                return vo;
            } else {
                return null;
            }
        }
        return null;
    }


    @Override
    public void expired() {
        // 查找出超过24小时还未领完的红包
        StringBuffer sb = new StringBuffer();
        sb.append("select * from red_packet where status='OPENDING' and  ctAt <unix_timestamp(now())-86400");
        List<RedPacket> list = dbService.list(RedPacket.class, sb.toString());
        // 未被领取的退回到钱包
        // 更新红包状态为 expired
        for (int i = 0; i < list.size(); i++) {
            RedPacket redPacket = list.get(i);
            redPacket.setStatus(RedPacketStatusEnum.EXPIRED);
            this.update(redPacket);
            userAccountRecordService.add(redPacket.getCurrencyId(), redPacket.getUserId(), redPacket.getTotal().subtract(redPacket.getOpened()), BizTypeEnum.EXPIRED_RED_PACKET, redPacket.getId(), redPacket.getNo());
            // 同时需要更新消息记录
            groupLogService.updateRedPacket(redPacket.getId(), RedPacketStatusEnum.EXPIRED);
        }
    }

    @Override
    public Pagination listPage(String conditionData, String currencyId, String type, String status, String keyword, String[] searchDate, int pageNumber, int pageSize) {
        Sql sql = Sqls.create("SELECT rp.*,u.nickName,u.userName,u.phone,sc.name As currencyName FROM red_packet AS rp")
                .appendSourceSql("LEFT JOIN user AS u ON rp.userId = u.id")
                .appendSourceSql("LEFT JOIN sys_currency AS sc ON rp.currencyId = sc.id $condition");
        Cnd cnd = Cnd.NEW();
        if (Strings.isNotBlank(conditionData)) {
            String[] split = conditionData.split(",");
            for (String col : split) {
                String colName = UserColEnum.getValByName(col);
                if (Strings.isNotBlank(colName)) {
                    // 默认不显示游客、内部账号
                    cnd.and("u." + colName, "=", 0);
                }
            }
        }
        if (Strings.isNotBlank(currencyId)) {
            cnd.and("sc.id", "=", currencyId);
        }
        if (Strings.isNotBlank(type)) {
            cnd.and("rp.type", "=", type);
        }
        if (Strings.isNotBlank(status)) {
            cnd.and("rp.status", "=", status);
        }
        if (Strings.isNotBlank(keyword)) {
            keyword = "%" + keyword + "%";
            cnd.and(Cnd.exps("u.nickName", "LIKE", keyword)
                    .or("u.userName", "LIKE", keyword)
                    .or("u.phone", "LIKE", keyword)
                    .or("rp.no", "LIKE", keyword)
            );
        }
        if (null != searchDate && searchDate.length != 0) {
            cnd.and("rp.ctAt", ">=", Times.d2TS(Times.D(searchDate[0])));
            cnd.and("rp.ctAt", "<=", Times.d2TS(Times.D(searchDate[1])));
        }
        sql.setCondition(cnd);
        sql.appendSourceSql("ORDER BY rp.ctAt DESC");
        return dbService.listPage(RedPacketVO.class, pageNumber, pageSize, sql.toString());
    }

    @Override
    public BigDecimal searchSum(String conditionData, String currencyId, String type, String status, String keyword, String[] searchDate) {
        StringBuffer sb = new StringBuffer();
        sb.append("select IFNULL(sum(r.total * c.rate), 0) from red_packet r left join sys_currency c on r.currencyId=c.id ");
        sb.append(" left join user u on u.id = r.userId ");
        sb.append(" where 1=1");
        if (Strings.isNotBlank(conditionData)) {
            String[] split = conditionData.split(",");
            for (String col : split) {
                String colName = UserColEnum.getValByName(col);
                if (Strings.isNotBlank(colName)) {
                    // 默认不显示游客、内部账号
                    sb.append(" and u.").append(colName).append(" = 0");
                }
            }
        }
        if (Strings.isNotBlank(keyword)) {
            sb.append(" and ( u.nickName like '%").append(keyword).append("%'");
            sb.append(" or u.phone like '%").append(keyword).append("%'");
            sb.append(" or u.userName like '%").append(keyword).append("%' ");
            sb.append(" or r.no like '%").append(keyword).append("%' )");
        }
        if (searchDate != null && searchDate.length > 0) {
            sb.append(" and r.ctAt >=").append(Times.d2TS(Times.D(searchDate[0])));
            sb.append(" and r.ctAt <=").append(Times.d2TS(Times.D(searchDate[1])));
        }
        if (Strings.isNotBlank(status)) {
            sb.append(" and r.status = '").append(status).append("'");
        }
        if (Strings.isNotBlank(type)) {
            sb.append(" and r.type = '").append(type).append("'");
        }
        if (Strings.isNotBlank(currencyId)) {
            sb.append(" and r.currencyId = '").append(currencyId).append("'");
        }

        Sql sql = Sqls.create(sb.toString());
        sql.setCallback(Sqls.callback.str());
        this.dao().execute(sql);
        return new BigDecimal(sql.getString()).setScale(6, BigDecimal.ROUND_DOWN);
    }
}
